package com.fs.server.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.fs.core.security.config.DBConfig;
import com.fs.core.security.util.SM2Utils;
import com.fs.core.security.util.SecurityConstant;
import com.fs.core.security.util.SpringUtil;
import com.fs.core.web.BaseResult;
import com.fs.server.service.RedisClientDetailsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Api(tags = "OAuth2相关操作")
@RestController
public class AuthController {
	
	private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
	
	@Resource
	private ObjectMapper objectMapper; // springmvc启动时自动装配json处理类
	
	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private TokenStore tokenStore;

	@Autowired
	private DBConfig dBConfig;

	@Autowired(required=false)
	private RedisTemplate<String, Object> redisTemplate ;

	@ApiOperation(value = "用户名密码获取token")
	@PostMapping("/oauth/user/token")
	public void getUserTokenInfo(
			@ApiParam(required = true, name = "loginAccount", value = "账号") @RequestParam(value = "loginAccount") String loginAccount,
			@ApiParam(required = true, name = "loginPwd", value = "密码") @RequestParam(value = "loginPwd") String loginPwd,
			HttpServletRequest request, HttpServletResponse response) {
		String clientId = request.getHeader("client_id");
		String clientSecret = request.getHeader("client_secret");
		try {
			if (clientId == null || "".equals(clientId)) {
				throw new UnapprovedClientAuthenticationException("请求头中无client_id信息");
			}
			if (clientSecret == null || "".equals(clientSecret)) {
				throw new UnapprovedClientAuthenticationException("请求头中无client_secret信息");
			}
			RedisClientDetailsService clientDetailsService = SpringUtil.getBean(RedisClientDetailsService.class);
			ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
			if (clientDetails == null) {
				throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在");
			} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
				throw new UnapprovedClientAuthenticationException("clientSecret不匹配");
			}
			TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(),
					"customer");
			OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
			UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginAccount, loginPwd);
			AuthenticationManager authenticationManager = SpringUtil.getBean(AuthenticationManager.class);
			Authentication authentication = authenticationManager.authenticate(token);
			SecurityContextHolder.getContext().setAuthentication(authentication);
			OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
			AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
					.getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);
			OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices
					.createAccessToken(oAuth2Authentication);
			oAuth2Authentication.setAuthenticated(true);
			response.setContentType("application/json;charset=UTF-8");
			response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
			response.getWriter().flush();
			response.getWriter().close();
		} catch (Exception e) {
			response.setStatus(HttpStatus.UNAUTHORIZED.value());
			response.setContentType("application/json;charset=UTF-8");
			Map<String, String> rsp = new HashMap<>();
			rsp.put("code", HttpStatus.UNAUTHORIZED.value() + "");
			rsp.put("msg", e.getMessage());
			try {
				response.getWriter().write(objectMapper.writeValueAsString(rsp));
				response.getWriter().flush();
				response.getWriter().close();
			} catch (JsonProcessingException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	
	@ApiOperation(value = "clientId获取token")
	@PostMapping("/oauth/client/token")
	public void getClientTokenInfo( HttpServletRequest request, HttpServletResponse response) {
		String clientId = request.getHeader("client_id");
		String clientSecret = request.getHeader("client_secret");
		try {
			if (clientId == null || "".equals(clientId)) {
				throw new UnapprovedClientAuthenticationException("请求参数中无clientId信息");
			}
			if (clientSecret == null || "".equals(clientSecret)) {
				throw new UnapprovedClientAuthenticationException("请求参数中无clientSecret信息");
			}
			RedisClientDetailsService clientDetailsService = SpringUtil.getBean(RedisClientDetailsService.class);
			ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
			if (clientDetails == null) {
				throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在");
			} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
				throw new UnapprovedClientAuthenticationException("clientSecret不匹配");
			}
			Map<String, String> map = new HashMap<>();
			map.put("client_secret", clientSecret);
			map.put("client_id", clientId);
			map.put("grant_type", "client_credentials");
			TokenRequest tokenRequest = new TokenRequest(map, clientId, clientDetails.getScope(), "client_credentials");
			OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
			AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
					.getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);
			OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
			ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(
					authorizationServerTokenServices, clientDetailsService, requestFactory);
			clientCredentialsTokenGranter.setAllowRefresh(true);
			OAuth2AccessToken oAuth2AccessToken = clientCredentialsTokenGranter.grant("client_credentials",
					tokenRequest);
			response.setContentType("application/json;charset=UTF-8");
			response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
			response.getWriter().flush();
			response.getWriter().close();
		} catch (Exception e) {
			response.setStatus(HttpStatus.UNAUTHORIZED.value());
			response.setContentType("application/json;charset=UTF-8");
			Map<String, String> rsp = new HashMap<>();
			rsp.put("code", HttpStatus.UNAUTHORIZED.value() + "");
			rsp.put("msg", e.getMessage());
			try {
				response.getWriter().write(objectMapper.writeValueAsString(rsp));
				response.getWriter().flush();
				response.getWriter().close();
			} catch (JsonProcessingException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	
	@ApiOperation(value = "access_token刷新token")
	@PostMapping(value = "/oauth/refresh/token", params = "access_token")
	public void refreshTokenInfo(String access_token ,HttpServletRequest request, HttpServletResponse response) {
		//拿到当前用户信息
    	try {
			Authentication user = SecurityContextHolder.getContext()
			        .getAuthentication();
			if(user!=null){
				if(user instanceof OAuth2Authentication){
					Authentication athentication = (Authentication)user;
					OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails() ;
				}
			}
			OAuth2AccessToken accessToken = tokenStore.readAccessToken(access_token);
			OAuth2Authentication  auth =(OAuth2Authentication ) user ;
			RedisClientDetailsService clientDetailsService = SpringUtil.getBean(RedisClientDetailsService.class);
			ClientDetails clientDetails = clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId());
			AuthorizationServerTokenServices authorizationServerTokenServices = SpringUtil
					.getBean("defaultAuthorizationServerTokenServices", AuthorizationServerTokenServices.class);
			OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
			RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter( authorizationServerTokenServices, clientDetailsService, requestFactory );
			Map<String, String> map = new HashMap<>();
			map.put("grant_type", "refresh_token");
			map.put("refresh_token", accessToken.getRefreshToken().getValue());
			TokenRequest tokenRequest = new TokenRequest(map, auth.getOAuth2Request().getClientId(), auth.getOAuth2Request().getScope()  , "refresh_token");
			OAuth2AccessToken oAuth2AccessToken = refreshTokenGranter.grant("refresh_token",
					tokenRequest);
			tokenStore.removeAccessToken(accessToken);
			response.setContentType("application/json;charset=UTF-8");
			response.getWriter().write(objectMapper.writeValueAsString(oAuth2AccessToken));
			response.getWriter().flush();
			response.getWriter().close();
		} catch (Exception e) {
			response.setStatus(HttpStatus.UNAUTHORIZED.value());
			response.setContentType("application/json;charset=UTF-8");
			Map<String, String> rsp = new HashMap<>();
			rsp.put("code", HttpStatus.UNAUTHORIZED.value() + "");
			rsp.put("msg", e.getMessage());
			try {
				response.getWriter().write(objectMapper.writeValueAsString(rsp));
				response.getWriter().flush();
				response.getWriter().close();
			} catch (JsonProcessingException e1) {
				e1.printStackTrace();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	/**
	 * 移除access_token和refresh_token
	 * @param access_token
	 */
	@ApiOperation(value = "移除token")
	@PostMapping(value = "/oauth/remove/token", params = "access_token")
	public BaseResult<Boolean> removeToken(String access_token) {

		//拿到当前用户信息
    	Authentication user = SecurityContextHolder.getContext()
                .getAuthentication();
		if(user!=null){
			if(user instanceof OAuth2Authentication){
				Authentication athentication = (Authentication)user;
				OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails() ;
			}
		}
		OAuth2AccessToken accessToken = tokenStore.readAccessToken(access_token);
		if (accessToken != null) {
			// 移除access_token
			tokenStore.removeAccessToken(accessToken);
			redisTemplate.delete(access_token);
			// 移除refresh_token
			if (accessToken.getRefreshToken() != null) {
				tokenStore.removeRefreshToken(accessToken.getRefreshToken());
			}
		}
		return new BaseResult<Boolean>(true);
	}

	@ApiOperation(value = "获取token信息")
		@PostMapping(value = "/oauth/get/token", params = "access_token")
	public OAuth2AccessToken getTokenInfo(String access_token) {
		//拿到当前用户信息
    	Authentication user = SecurityContextHolder.getContext()
                .getAuthentication();
		if(user!=null){
			if(user instanceof OAuth2Authentication){
				Authentication athentication = (Authentication)user;
				OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) athentication.getDetails() ;
			}
		}
		OAuth2AccessToken accessToken = tokenStore.readAccessToken(access_token);
		return accessToken ;
	}
	
	/**
	 * 当前登陆用户信息
	 * security获取当前登录用户的方法是SecurityContextHolder.getContext().getAuthentication()
	 * 这里的实现类是org.springframework.security.oauth2.provider.OAuth2Authentication
	 * @return
	 */
	@ApiOperation(value = "当前登陆用户信息")
	@RequestMapping(value = { "/oauth/userinfo" }, produces = "application/json") // 获取用户信息。/auth/user
	public BaseResult<Object> getCurrentUserDetail() {
		return new BaseResult<Object>(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
	}

	@ApiOperation(value="获取安全配置信息", notes="")
	@PostMapping("/oauth/getGlobalConfig")
	public BaseResult<Map<String,Object>> getGlobalConfig() {
		Map<String,Object> info = new HashMap<>();

		//获取加密字段
		String base64Encryption = dBConfig.getBase64Encryption();
		String[] base64EncryptionFields = null;
		if(base64Encryption != null && base64Encryption.length() > 0){
			base64EncryptionFields = base64Encryption.split(",");
		}
		info.put("isSafe", dBConfig.getIsTransSafe());//获取是否安全模式
		info.put("isXssSafe",  dBConfig.getIsXssSafe());//获取是否xss安全模式
		info.put("isModelSafe",  dBConfig.getIsModelSafe());//获取是否model和token安全模式
		info.put("decKey",  dBConfig.getDecKey());//解密密钥
		info.put("exponent", SecurityConstant.EXPONENT);
		info.put("modulus", SecurityConstant.MODULUS);
		info.put("cryptType", dBConfig.getCryptType());//加密类型
		info.put("cipherMode", dBConfig.getCipherMode());//sm2加密模式
		info.put("pubkeyHex", SM2Utils.DEFAULT_pubk);//sm2公钥
		info.put("base64Encryption", base64EncryptionFields);
		info.put("encryptionRequest", dBConfig.getEncryptionRequest());//获取待加密请求

		return new BaseResult<>(info);
	}
}